﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using TableParsers;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Diagnostics;
using System.Windows.Input;
using System.Windows.Documents;

using SpeakupBackend.Themes;
using SpeakupBackend;

namespace Frontend
{
    enum HeaderType { ClassType, Level, Week };

    //database part
    partial class  WorkshopThemeGrid: BorderedGrid
    {
        private List<Week> m_supportedWeeks;
        private List<Week> SupportedWeeks
        {
            get
            {
                if (m_supportedWeeks == null)
                {
                    m_supportedWeeks = (from n in Week.GetWeeks()
                                        orderby n ascending
                                        select n).ToList();
                }
                return m_supportedWeeks;
            }
        }

        private List<LevelGroup> m_supportedLevels;
        private List<LevelGroup> SupportedLevels
        {
            get
            {
                if (m_supportedLevels == null)
                {
                    m_supportedLevels = (from n in LevelGroup.DefaultFactory.GetLevelGroups()
                                         orderby n ascending
                                         select n).ToList();
                }
                return m_supportedLevels;
            }
        }

        private List<WorkshopClassType> m_supportedClassTypes;
        private List<WorkshopClassType> SupportedClassTypes
        {
            get
            {
                if (m_supportedClassTypes == null)
                {
                    m_supportedClassTypes = (from n in WorkshopClassType.GetWorkshopClassTypes()
                                             orderby n ascending
                                             select n).ToList();
                }

                return m_supportedClassTypes;
            }
        }

        private WorkshopThemeCollection.ThemeFilter m_weekThemeFilter;
        private WorkshopThemeCollection.ThemeFilter WeekThemeFilter
        {
            get
            {
                return m_weekThemeFilter;
            }
            set
            {
                if (value != m_weekThemeFilter)
                {
                    m_workshopWeekThemes = null;
                    m_weekThemeFilter = value;
                }
            }
        }

        private List<WorkshopTheme> m_workshopWeekThemes;
        private List<WorkshopTheme> WorkshopWeekThemes
        {
            get
            {
                if (m_workshopWeekThemes == null)
                {
                    m_workshopWeekThemes = BackendCtx.WorkshopThemes.ApplyFilter(WeekThemeFilter).ToList();
                }
                return m_workshopWeekThemes;
            }
        }

        BackendContext m_backendCtx;
        public BackendContext BackendCtx
        {
            get
            {
                return m_backendCtx;
            }
            set
            {
                if (m_backendCtx != value)
                {
                    m_backendCtx = value;
                    m_supportedWeeks = null;
                    m_supportedLevels = null;
                    m_supportedClassTypes = null;
                    m_workshopWeekThemes = null;
                }
            }
        }
    }

    //grid setup/refresh part
    partial class WorkshopThemeGrid
    {
        static WorkshopThemeGrid()
        {
            FrameworkPropertyMetadata metadata = new FrameworkPropertyMetadata();
            metadata.DefaultValue = null;
            WeekThemeProperty = DependencyProperty.RegisterAttached("WeekTheme",
                                                                    typeof(WorkshopTheme),
                                                                    typeof(WorkshopThemeGrid),
                                                                    metadata);
        }

        #region Attached Property "WeekTheme"

        public static readonly DependencyProperty WeekThemeProperty;        
        public static void SetWeekTheme(UIElement uiElement, WorkshopTheme weekTheme)
        {
            uiElement.SetValue(WeekThemeProperty, weekTheme);
        }
        public static WorkshopTheme GetWeekTheme(UIElement uiElement)
        {
            return (WorkshopTheme)uiElement.GetValue(WeekThemeProperty);
        }

        #endregion

        public HeaderType VerticalHeaderType { get; private set; }
        public HeaderType HorizontalHeaderType { get; private set; }

        private List<UIElement> ColumnHeaders = new List<UIElement>();
        private List<UIElement> RowHeaders = new List<UIElement>();
        private List<UIElement> ContentItems = new List<UIElement>();

        private UIElementFactory UIElementFactory = new UIElementFactory();

        int GetOffset(WorkshopTheme weekTheme, HeaderType headerType)
        {
            int offset = 0;
            switch (headerType)
            {
                case HeaderType.ClassType:
                    offset = SupportedClassTypes.IndexOf( weekTheme.ClassType );
                    break;
                case HeaderType.Level:
                    offset = SupportedLevels.IndexOf( weekTheme.LevelGroup );
                    break;
                case HeaderType.Week:
                    offset = SupportedWeeks.IndexOf( weekTheme.Week );
                    break;
                default:
                    throw new ArgumentException("Unknown column headerName type");
            }

            return offset;
        }
        
        Week CurrentWeek;
        Level CurrentLevel;
        WorkshopClassType CurrentClassType;

        TextBlock SwitchLink;

        public void SetupGrid(HeaderType vertical, HeaderType horizontal,
                              Week week, Level level, WorkshopClassType classType)
        {
            if (vertical == horizontal)
            {
                throw new ArgumentException("Vertical headerName is the same as horizontal.");
            }

            List<HeaderType> headerTypes = new List<HeaderType> { vertical, horizontal };

            if (headerTypes.Contains(HeaderType.ClassType) && 
                headerTypes.Contains(HeaderType.Week))
            {
                if (level == null)
                {
                    throw new ArgumentException("Level is not specified.");
                }
                
                WeekThemeFilter = WorkshopThemeCollection.LevelFilter(level.LevelNumber);
            }
            else if (headerTypes.Contains(HeaderType.ClassType) && 
                     headerTypes.Contains(HeaderType.Level))
            {
                if (week == null)
                {
                    throw new ArgumentException("Week is not specified.");
                }

                WeekThemeFilter = WorkshopThemeCollection.WeekFilter(week.WeekNumber);
            }
            else if (headerTypes.Contains(HeaderType.Week) &&
                     headerTypes.Contains(HeaderType.Level))
            {
                if (classType == null)
                {
                    throw new ArgumentException("ClassType is not specified");
                }

                WeekThemeFilter = WorkshopThemeCollection.ClassTypeFilter(classType.ClassType);
            }
            else
            {
                throw new ArgumentException("Header type combination is not supported");
            }

            VerticalHeaderType = vertical;
            HorizontalHeaderType = horizontal;

            CurrentWeek = week;
            CurrentLevel = level;
            CurrentClassType = classType;

            SetupCurrentRowAndColumn(vertical, horizontal, week, level, classType);
            RefreshGrid();

            if (SwitchLink == null)
            {
                Hyperlink hyperlink = new Hyperlink();
                hyperlink.Inlines.Add("<->");
                hyperlink.Click += new RoutedEventHandler(SwitchRowsAndColumns_Click);

                SwitchLink = new TextBlock();
                SwitchLink.TextAlignment = TextAlignment.Center;
                SwitchLink.VerticalAlignment = VerticalAlignment.Center;
                SwitchLink.HorizontalAlignment = HorizontalAlignment.Center;
                SwitchLink.Margin = new Thickness(2);
                SwitchLink.Inlines.Add(hyperlink);
                
                Children.Add(SwitchLink);
            }
        }

        void SwitchRowsAndColumns_Click(object sender, RoutedEventArgs e)
        {
            SetupGrid(HorizontalHeaderType, VerticalHeaderType, CurrentWeek, CurrentLevel, CurrentClassType);
        }


        void RefreshColumnHeaders()
        {
            //clear
            ColumnDefinitions.Clear();

            foreach (var el in ColumnHeaders)
            {
                Children.Remove(el);
            }
            ColumnHeaders.Clear();

            //fill
            ColumnHeaders = new List<UIElement>();
            switch (HorizontalHeaderType)
            {
                case HeaderType.ClassType:
                    foreach (var ct in SupportedClassTypes)
                    {
                        ColumnHeaders.Add(UIElementFactory.CreateClassType(ct));
                    }
                    break;
                case HeaderType.Level:
                    foreach (var lvl in SupportedLevels)
                    {
                        ColumnHeaders.Add(UIElementFactory.CreateLevelGroup(lvl));
                    }
                    break;
                case HeaderType.Week:
                    foreach (var w in SupportedWeeks)
                    {
                        ColumnHeaders.Add(UIElementFactory.CreateWeek(w));
                    }
                    break;
                default:
                    throw new ArgumentException("Unknown column headerName type");
            }

            //update ui
            ColumnDefinition coldef = new ColumnDefinition();
            
            coldef.Width = GridLength.Auto;
            ColumnDefinitions.Add(coldef);

            for (int i = 0; i < ColumnHeaders.Count; i++)
            {
                coldef = new ColumnDefinition();
                ColumnDefinitions.Add(coldef);
            }

            for (int i = 0; i < ColumnHeaders.Count; i++)
            {
                Grid.SetRow(ColumnHeaders[i], 0);
                Grid.SetColumn(ColumnHeaders[i], i+1);
                Children.Add(ColumnHeaders[i]);
            }
            
        }
        void RefreshRowHeaders()
        {
            //clear
            RowDefinitions.Clear();

            foreach (var el in RowHeaders)
            {
                Children.Remove(el);
            }
            RowHeaders.Clear();

            //fill           
            switch (VerticalHeaderType)
            {
                case HeaderType.ClassType:
                    foreach (var ct in SupportedClassTypes)
                    {
                        RowHeaders.Add(UIElementFactory.CreateClassType(ct));
                    }
                    break;
                case HeaderType.Level:
                    foreach (var lvl in SupportedLevels)
                    {
                        RowHeaders.Add(UIElementFactory.CreateLevelGroup(lvl));
                    }
                    break;
                case HeaderType.Week:
                    foreach (var w in SupportedWeeks)
                    {
                        RowHeaders.Add(UIElementFactory.CreateWeek(w));
                    }
                    break;
                default:
                    throw new ArgumentException("Unknown row headerName type");
            }

            //update ui
            RowDefinition rowdef = new RowDefinition();

            rowdef.Height = GridLength.Auto;
            RowDefinitions.Add(rowdef);

            for (int i = 0; i < RowHeaders.Count; i++)
            {
                rowdef = new RowDefinition();
                RowDefinitions.Add(rowdef);
            }

            for (int i = 0; i < RowHeaders.Count; i++)
            {
                Grid.SetColumn(RowHeaders[i], 0);
                Grid.SetRow(RowHeaders[i], i + 1);
                Children.Add(RowHeaders[i]);
            }
        }
        void RefreshContentItems()
        {
            foreach (var a in ContentItems)
            {
                Children.Remove(a);
            }
            ContentItems.Clear();

            foreach (WorkshopTheme theme in WorkshopWeekThemes)
            {
                UIElement ui = UIElementFactory.CreateWorkshopTheme(theme);
                Grid.SetRow(ui, 1 + GetOffset(theme, VerticalHeaderType));
                Grid.SetColumn(ui, 1 + GetOffset(theme, HorizontalHeaderType));

                WorkshopThemeGrid.SetWeekTheme(ui, theme);
                ContentItems.Add(ui);
                Children.Add(ui);                
            }
        }
        void RefreshGrid()
        {
            RefreshColumnHeaders();
            RefreshRowHeaders();
            RefreshContentItems();

            InvalidateVisual();
        }
    }

    //mouse management, rendering
    partial class WorkshopThemeGrid
    {
        public WorkshopThemeGrid()
        {
            this.MouseMove += WeekThemeGrid_MouseMove;
        }

        WorkshopTheme m_highlightedWeekTheme = null;
        public WorkshopTheme HighlightedWeekTheme
        {
            get
            {
                return m_highlightedWeekTheme;
            }
            private set
            {
                if (m_highlightedWeekTheme != value)
                {
                    m_highlightedWeekTheme = value;
                    OnHighlitedWeekThemeChanged();

                    InvalidateVisual();
                }
            }
        }

        public event EventHandler<EventArgs> HighlitedWeekThemeChanged;
        protected void OnHighlitedWeekThemeChanged()
        {
            if (HighlitedWeekThemeChanged != null)
            {
                HighlitedWeekThemeChanged(this, new EventArgs());
            }
        }

        Brush CurrentRectBrush = new SolidColorBrush(Color.FromArgb(50, 100, 200, 100));
        Pen CurrentRectPen = new Pen(Brushes.Black, 1.5);

        Brush HighlightRectBrush = Brushes.PeachPuff;
        Pen HighlightRectPen = new Pen(Brushes.Black, 2);

        private int? CurrentRow = null;
        private int? CurrentColumn = null;

        private int? HighlightedRow = null;
        private int? HighlightedColumn = null;

        Rect? GetRowRect(int rowIndex)
        {
            if (rowIndex >= RowDefinitions.Count) return null;

            double height = 0;
            int curRowIndex = 0;
            foreach (var r in this.RowDefinitions)
            {
                if (curRowIndex == rowIndex)
                {
                    return new Rect(0, height, RenderSize.Width, r.ActualHeight);                    
                }
                curRowIndex++;
                height += r.ActualHeight;
            }

            return null;
        }
        Rect? GetColumnRect(int columnIndex)
        {
            if (columnIndex >= ColumnDefinitions.Count) return null;

            double width = 0;
            int curColumnIndex = 0;
            foreach (var c in this.ColumnDefinitions)
            {
                if (curColumnIndex == columnIndex)
                {
                    return new Rect(width, 0, c.ActualWidth, RenderSize.Height);
                }
                curColumnIndex++;
                width += c.ActualWidth;
            }

            return null;
        }
        Rect? GetRect(int rowIndex, int columnIndex)
        {
            Rect? rowRect = GetRowRect(rowIndex);
            Rect? columnRect = GetColumnRect(columnIndex);
            Rect defaultRect = new Rect();

            if (rowRect == null || columnRect == null)
            {
                return null;
            }

            return Rect.Intersect(rowRect ?? defaultRect, columnRect ?? defaultRect);
        }

        int? GetMouseRow(double mouseY)
        {
            double height = 0;
            int row = 0;
            foreach (var r in this.RowDefinitions)
            {
                if (height < mouseY && mouseY < height + r.ActualHeight)
                {
                    return row;
                }
                height += r.ActualHeight;
                row++;
            }
            return null;
        }
        int? GetMouseColumn(double mouseX)
        {            
            double width = 0;
            int column = 0;
            foreach (var c in this.ColumnDefinitions)
            {
                if (width < mouseX && mouseX < width + c.ActualWidth)
                {
                    return column;
                }
                column++;
                width += c.ActualWidth;
            }

            return null;
        }

        void SetupCurrentRowAndColumn(HeaderType vertical, HeaderType horizontal,
                              Week week, Level level, WorkshopClassType classType)
        {
            int? weekOffset = null;
            int? levelOffset = null;
            int? classTypeOffset = null;

            if (week != null)
            {
                weekOffset = SupportedWeeks.IndexOf( Week.GetWeek(week.WeekNumber) ) + 1;
            }

            if (level != null)
            {
                int index = 0;
                SupportedLevels.ForEach( (L)=> 
                { 
                    if(L.ContainsLevel(level))
                    {
                        levelOffset = index + 1;
                    }
                    index++;
                });                
            }

            if (classType != null)
            {
                classTypeOffset = SupportedClassTypes.IndexOf(classType) + 1;
            }


            switch (vertical)
            {
                case HeaderType.Week:
                    CurrentRow = weekOffset;
                    break;
                case HeaderType.Level:
                    CurrentRow = levelOffset;
                    break;
                case HeaderType.ClassType:
                    CurrentRow = classTypeOffset;
                    break;
            }

            switch (horizontal)
            {
                case HeaderType.Week:
                    CurrentColumn = weekOffset;
                    break;
                case HeaderType.Level:
                    CurrentColumn = levelOffset;
                    break;
                case HeaderType.ClassType:
                    CurrentColumn = classTypeOffset;
                    break;
            }
        }
        
        protected override void OnRender(System.Windows.Media.DrawingContext dc)
        {
            Rect defaultRect = new Rect();

            if (CurrentColumn != null)
            {
                Rect? rect = GetColumnRect(CurrentColumn??0);
                dc.DrawRectangle(CurrentRectBrush, CurrentRectPen, rect??defaultRect);
            }

            if (CurrentRow!= null)
            {
                Rect? rect = GetRowRect(CurrentRow ?? 0);
                dc.DrawRectangle(CurrentRectBrush, CurrentRectPen, rect ?? defaultRect);
            }

            if (HighlightedColumn != null && HighlightedColumn != 0)
            {
                Rect? rect = GetRect(0, HighlightedColumn ?? 0);
                dc.DrawRectangle(HighlightRectBrush, HighlightRectPen, rect ?? defaultRect);
            }

            if (HighlightedRow != null && HighlightedRow != 0)
            {

                Rect? rect = GetRect(HighlightedRow ?? 0, 0);
                dc.DrawRectangle(HighlightRectBrush, HighlightRectPen, rect ?? defaultRect);
            }

            base.OnRender(dc);           
        }

        void WeekThemeGrid_MouseMove(object sender, MouseEventArgs e)
        {
            Point pos = e.GetPosition(this);

            if (pos.X < 0 || pos.Y < 0 || pos.X > RenderSize.Width || pos.Y > RenderSize.Height)
            {
                Mouse.Capture(this, CaptureMode.None);
                return;
            }

            int? row = GetMouseRow(pos.Y);
            int? column = GetMouseColumn(pos.X);

            if (row != HighlightedRow || column != HighlightedColumn)
            {
                HighlightedRow = row;
                HighlightedColumn = column;


                if (row != null && column != null)
                {
                    HighlightedWeekTheme = (from n in this.ContentItems
                                            where Grid.GetColumn(n) == column && Grid.GetRow(n) == row
                                            select WorkshopThemeGrid.GetWeekTheme(n))
                                            .FirstOrDefault();

                }
                InvalidateVisual();
            }            
        }
        protected override void OnMouseLeave(System.Windows.Input.MouseEventArgs e)
        {
            HighlightedWeekTheme = null;

            HighlightedRow = null;
            HighlightedColumn = null;
            InvalidateVisual();
            base.OnMouseLeave(e);
        }
        protected override void OnMouseEnter(MouseEventArgs e)
        {
            Mouse.Capture(this, CaptureMode.SubTree);
            base.OnMouseEnter(e);
        }
    }
}
